package admin.server.service.impl;

import admin.dao.model.*;
import admin.server.feign.FeishuJavaFeign;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.crypto.symmetric.AES;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import admin.common.constant.AuthConstant;
import admin.common.domain.Client;
import admin.common.domain.dto.UserDto;
import admin.common.domain.req.Oauth2LoginReq;
import admin.common.domain.rsp.R;
import admin.common.domain.rsp.UserInfoRsp;
import admin.common.domain.rsp.UserRsp;
import admin.common.domain.rsp.UsersRsp;
import admin.common.enums.LoginMode;
import admin.common.enums.ResponseEnum;
import admin.common.properties.OauthProperties;

import admin.common.vo.PageVo;
import admin.common.vo.ResponseVO;
import admin.server.dao.UmsAdminDao;
import admin.server.dao.UmsAdminRoleRelationDao;
import admin.dao.mapper.UmsAdminLoginLogMapper;
import admin.dao.mapper.UmsAdminMapper;
import admin.dao.mapper.UmsAdminRoleRelationMapper;
import admin.dao.mapper.UmsRoleMapper;
import admin.server.exception.Asserts;
import admin.server.feign.AuthFeign;
import admin.server.model.dto.UmsAdminDetailDto;
import admin.server.model.dto.UmsAdminDto;
import admin.server.model.param.UmsAdminLoginParam;
import admin.server.model.param.UmsAdminRegisterParam;
import admin.server.model.param.UmsAdminUpdateParam;
import admin.server.model.param.UpdateAdminPasswordParam;
import admin.server.service.UmsAdminCacheService;
import admin.server.service.UmsAdminService;
import admin.web.utils.CurrentUtil;
import admin.redis.service.RedisService;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * UmsAdminService实现类
 * Created by zgb on 2018/4/26.
 */
@Slf4j
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UmsAdminServiceImpl.class);
    @Autowired
    private UmsAdminMapper adminMapper;
    @Autowired
    private UmsAdminRoleRelationMapper adminRoleRelationMapper;
    @Autowired
    private UmsAdminRoleRelationDao adminRoleRelationDao;
    @Autowired
    private UmsAdminLoginLogMapper loginLogMapper;
    @Autowired
    private AuthFeign authFeign;

    @Autowired
    private OauthProperties oauthProperties;

    @Autowired
    private FeishuJavaFeign feishuJavaFeign;
    @Autowired
    private UmsRoleMapper roleMapper;


    @Autowired
    private UmsAdminDao umsAdminDao;

    @Autowired
    private RedisService redisService;
    @Autowired
    private UmsAdminCacheService umsAdminCacheService;


    @Value("${services.admin.contextPath}")
    private String contextPath;

    @Value("${auth.canFeishuCodeRegisteYn}")
    private Boolean canFeishuCodeRegisteYn;


    @Value("${aes.key}")
    private String key;
    @Value("${aes.salt}")
    private String salt;

    private AES aes;


    @PostConstruct
    public void init() {
        // 构建
        aes = new AES("CBC", "PKCS7Padding",
                // 密钥，可以自定义
                key.getBytes(),
                // iv加盐，按照实际需求添加
                salt.getBytes());
    }

    public void uniqueCheck(UmsAdmin record) throws Exception {
        if (StringUtils.isBlank(record.getFeiShuOpenId())) {
            throw new Exception("飞书员工 必填！");
        }
        //更新，不变
        if (record.getId() != null) {
            UmsAdmin one = adminMapper.selectByPrimaryKey(record.getId());
            if (Objects.equals(one.getFeiShuOpenId(), record.getFeiShuOpenId()) && one.getLogicDeleteAt().equals(0L)) {
                return;
            }
        }
        UmsAdminExample umsAdminExample = new UmsAdminExample();
        umsAdminExample.createCriteria().andFeiShuOpenIdEqualTo(record.getFeiShuOpenId()).andLogicDeleteAtEqualTo(0L);
        List<UmsAdmin> list = adminMapper.selectByExample(umsAdminExample);
        if (!list.isEmpty()) {
            throw new Exception("飞书员工 已存在 请勿重复添加！");
        }
    }


    @Override
    public UmsAdmin getAdminByUsername(String username) {
        UmsAdminExample example = new UmsAdminExample();
        example.createCriteria().andUsernameEqualTo(username).andLogicDeleteAtEqualTo(0L);
        List<UmsAdmin> adminList = adminMapper.selectByExample(example);
        if (adminList != null && adminList.size() > 0) {
            return adminList.get(0);
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UmsAdmin register(UmsAdminRegisterParam umsAdminRegisterParam, HttpServletRequest request) throws Exception {
        uniqueCheck(BeanUtil.toBean(umsAdminRegisterParam, UmsAdmin.class));
        UmsAdmin umsAdmin = new UmsAdmin();
        umsAdmin.setNote(umsAdminRegisterParam.getNote());
        umsAdmin.setStatus(umsAdminRegisterParam.getStatus());
        umsAdmin.setCreateTime(new Date());
        UmsAdmin currentAdmin = getCurrentAdmin(request);
        //获取飞书code
        String feiShuCode = (String) redisService.get(AuthConstant.FEISHU_LOGIN_ADMINID_PREFIX.concat(":").concat(Long.toString(currentAdmin.getId())));
        if (StringUtils.isBlank(feiShuCode)) {
            throw new Exception("从redis 获取飞书code 失败： null");
        }
        String userAccessToken = feishuJavaFeign.userAccessToken(feiShuCode);
        log.info("userAccessToken:{},feiShuCode:{},feiShuOpenId:{},", userAccessToken, feiShuCode, umsAdminRegisterParam.getFeiShuOpenId());
        R<UsersRsp> usersRspR = feishuJavaFeign.users(umsAdminRegisterParam.getFeiShuOpenId(), AuthConstant.JWT_TOKEN_PREFIX.concat(userAccessToken));
        if (usersRspR == null) {
            Asserts.fail("调用 飞书 users 接口异常:无返回");
        }
        if (usersRspR.getCode() != 0) {
            Asserts.fail(String.format("调用 飞书 users 接口异常:返回码 %s，异常信息 %s%s", usersRspR.getCode(), usersRspR.getMsg(), usersRspR.getMessage()));
        }

        if (usersRspR.getData() == null || usersRspR.getData().getUser() == null) {
            Asserts.fail("调用 飞书 users 接口异常：无返回 user");
        }
        UserRsp user = usersRspR.getData().getUser();
        umsAdmin.setIcon(user.getAvatar().getAvatar72());
        umsAdmin.setNickName(user.getName());
        umsAdmin.setPhone(user.getMobile().replace("+", ""));
        umsAdmin.setFeiShuUnionId(user.getUnionId());
        umsAdmin.setFeiShuOpenId(user.getOpenId());
        //用于飞书扫码后内部登录
        umsAdmin.setUsername(user.getMobile().replace("+", ""));
        umsAdmin.setPassword(SecureUtil.md5(user.getOpenId()));
        //将密码进行加密操作
        String encodePassword = SecureUtil.md5(umsAdmin.getPassword());
        umsAdmin.setPassword(encodePassword);
        umsAdmin.setLogicDeleteAt(0L);
        adminMapper.insertSelective(umsAdmin);
        //分配角色
        Set<Long> roleIds = new HashSet<>();
        roleIds.addAll(umsAdminRegisterParam.getRoleIdList());
        for (Long roleId : roleIds) {
            UmsAdminRoleRelation umsAdminRoleRelation = new UmsAdminRoleRelation();
            umsAdminRoleRelation.setAdminId(umsAdmin.getId());
            umsAdminRoleRelation.setRoleId(roleId);
            adminRoleRelationMapper.insert(umsAdminRoleRelation);
        }
        return umsAdmin;
    }


    private void feishuLogin(UmsAdminLoginParam umsAdminLoginParam) throws Exception {
        String feiShuCode = umsAdminLoginParam.getFeiShuCode();
        if (StrUtil.isEmpty(feiShuCode)) {
            Asserts.fail("飞书授权码为空，必填");
        }
        //飞书扫码登录
        //获取飞书union_id
        String userAccessToken = feishuJavaFeign.userAccessToken(feiShuCode);
        R<UserInfoRsp> userInfoRspR = feishuJavaFeign.userInfo(AuthConstant.JWT_TOKEN_PREFIX.concat(userAccessToken));

        if (userInfoRspR == null) {
            Asserts.fail("调用 飞书 userInfo 接口异常:无返回");
        }
        if (userInfoRspR.getCode() != 0) {
            Asserts.fail(String.format("调用 飞书 userInfo 接口异常:返回码 %s，异常信息 %s%s", userInfoRspR.getCode(), userInfoRspR.getMsg(), userInfoRspR.getMessage()));
        }
        if (userInfoRspR.getData() == null || StrUtil.isEmpty(userInfoRspR.getData().getMobile())) {
            Asserts.fail("调用 飞书 userInfo 接口异常：无返回 mobile");
        }
        if (userInfoRspR.getData() == null || StrUtil.isEmpty(userInfoRspR.getData().getUnionId())) {
            Asserts.fail("调用 飞书 userInfo 接口异常：无返回 unionId");
        }
        //绑定飞书账号
        String mobile = userInfoRspR.getData().getMobile();
        mobile = mobile.replace("+", "");
        String unionId = userInfoRspR.getData().getUnionId();
        String openId = userInfoRspR.getData().getOpenId();
        UmsAdmin umsAdmin = umsAdminDao.queryByPhone(mobile);
        Long adminId = null;
        if (umsAdmin == null) {
            if (canFeishuCodeRegisteYn) {
                //新用户扫码注册: 直接插入
                UmsAdmin umsAdminAdd = new UmsAdmin();
                umsAdminAdd.setPhone(mobile);
                umsAdminAdd.setFeiShuUnionId(unionId);
                umsAdminAdd.setFeiShuOpenId(openId);
                umsAdminAdd.setUsername(mobile);
                umsAdminAdd.setNickName(userInfoRspR.getData().getName());
                umsAdminAdd.setIcon(userInfoRspR.getData().getAvatarThumb());
                umsAdminAdd.setPassword(SecureUtil.md5(unionId));
                umsAdminAdd.setStatus(true);
                umsAdminAdd.setCreateTime(new Date());
                umsAdminAdd.setLogicDeleteAt(0L);
                adminMapper.insert(umsAdminAdd);
                adminId = umsAdminAdd.getId();
                //分配角色
                List<Long> roleIds = new ArrayList<>();
                UmsRoleExample umsRoleExample = new UmsRoleExample();
                umsRoleExample.createCriteria().andFeiShuCodeRegisteNecessaryYnEqualTo(true);
                List<UmsRole> umsRoles = roleMapper.selectByExample(umsRoleExample);
                roleIds.addAll(umsRoles.stream().map(e -> e.getId()).collect(Collectors.toList()));
                this.updateRole(umsAdminAdd.getId(), roleIds);
                umsAdmin = umsAdminAdd;

            } else {
                throw new Exception("暂无此账号，请联系管理员添加");
            }
        } else {
            adminId = umsAdmin.getId();
            //表单注册是必须填写手机号、username、password，通过手机号绑定飞书unionId
            if (canFeishuCodeRegisteYn && StrUtil.isNotEmpty(umsAdmin.getFeiShuOpenId())) {
                umsAdminDao.bindingFeiShuQueryByPhone(mobile, unionId, openId);
            }
        }
        //绑定飞书code
        String key = new StringBuffer().append(AuthConstant.FEISHU_USERACCESSTOKEN_PREFIX).append(":").append(feiShuCode).toString();
        Long expire = redisService.getExpire(key);
        redisService.set(AuthConstant.FEISHU_LOGIN_ADMINID_PREFIX.concat(":").concat(Long.toString(adminId)), feiShuCode, expire);
        umsAdminLoginParam.setUsername(umsAdmin.getUsername());
        umsAdminLoginParam.setPassword(umsAdmin.getPassword());
        umsAdminLoginParam.setPasswordEncodeYn(true);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseVO login(UmsAdminLoginParam umsAdminLoginParam) throws Exception {
        String feiShuCode = umsAdminLoginParam.getFeiShuCode();
        if (Objects.equals(LoginMode.PASSWORD.getCode(), umsAdminLoginParam.getLoginModeValue())) {
            if (StrUtil.isEmpty(umsAdminLoginParam.getUsername()) || StrUtil.isEmpty(umsAdminLoginParam.getPassword())) {
                Asserts.fail("用户名或密码不能为空！");
            }
        }
        if (Objects.equals(LoginMode.FEISHU_UNION_ID.getCode(), umsAdminLoginParam.getLoginModeValue())) {
            if (StrUtil.isEmpty(feiShuCode)) {
                Asserts.fail("飞书扫码登录不能为空！");
            }
            feishuLogin(umsAdminLoginParam);
        }
        //最终登录入口：账号密码登录
        Boolean passwordEncodeYn = umsAdminLoginParam.getPasswordEncodeYn();
        List<Client> clients = oauthProperties.getClients();
        PathMatcher pathMatcher = new AntPathMatcher();
        Client currentClient = clients.stream().filter(e -> pathMatcher.match(e.getUrlPattern(), contextPath)).findFirst().orElse(null);
        Oauth2LoginReq oauth2LoginReq = new Oauth2LoginReq().setClient_id(currentClient.getId()).setClient_secret(currentClient.getSecret()).setGrant_type("password")
                .setUsername(umsAdminLoginParam.getUsername()).setPassword(umsAdminLoginParam.getPassword()).setPasswordEncodeYn(passwordEncodeYn);
        ResponseVO restResult = authFeign.entry(oauth2LoginReq);
        if (ResponseEnum.SUCCESS.getCode() == restResult.getCode() && restResult.getData() != null) {
            insertLoginLog(umsAdminLoginParam.getUsername());
            updateLoginTimeByUsername(umsAdminLoginParam.getUsername());
        }
        return restResult;
    }

    @Override
    public ResponseVO refreshToken(String refreshToken) throws Exception {
        List<Client> clients = oauthProperties.getClients();
        PathMatcher pathMatcher = new AntPathMatcher();
        Client currentClient = clients.stream().filter(e -> pathMatcher.match(e.getUrlPattern(), contextPath)).findFirst().orElse(null);
        Oauth2LoginReq oauth2LoginReq = new Oauth2LoginReq().setClient_id(currentClient.getId()).setClient_secret(currentClient.getSecret()).setGrant_type("refresh_token")
                .setRefresh_token(refreshToken);
        ResponseVO restResult = authFeign.entry(oauth2LoginReq);
        return restResult;
    }

    public void logout(String authHeader) {
        authFeign.logout(authHeader);
    }

    public void kick(String userName) {
        authFeign.kick(userName);
    }


    /**
     * 根据用户名修改登录时间
     */
    private void updateLoginTimeByUsername(String username) {
        UmsAdmin record = new UmsAdmin();
        record.setLoginTime(new Date());
        UmsAdminExample example = new UmsAdminExample();
        example.createCriteria().andUsernameEqualTo(username).andLogicDeleteAtEqualTo(0L);
        adminMapper.updateByExampleSelective(record, example);
    }

    /**
     * 添加登录记录
     *
     * @param username 用户名
     */
    private void insertLoginLog(String username) {
        UmsAdmin admin = getAdminByUsername(username);
        if (admin == null) return;
        UmsAdminLoginLog loginLog = new UmsAdminLoginLog();
        loginLog.setAdminId(admin.getId());
        loginLog.setUserName(admin.getUsername());
        loginLog.setNickName(admin.getNickName());
        loginLog.setCreateTime(new Date());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        loginLog.setIp(request.getRemoteAddr());
        loginLogMapper.insert(loginLog);
    }


    @Override
    public UmsAdmin getItem(Long id) {
        return adminMapper.selectByPrimaryKey(id);
    }

    @Override
    public UmsAdminDetailDto detail(Long id) {
        UmsAdmin umsAdmin = adminMapper.selectByPrimaryKey(id);
        UmsAdminDetailDto umsAdminDetailDto = BeanUtil.toBean(umsAdmin, UmsAdminDetailDto.class);
        UmsAdminRoleRelationExample umsAdminRoleRelationExample = new UmsAdminRoleRelationExample();
        umsAdminRoleRelationExample.createCriteria().andAdminIdEqualTo(id);
        List<UmsAdminRoleRelation> umsAdminRoleRelations = adminRoleRelationMapper.selectByExample(umsAdminRoleRelationExample);
        umsAdminDetailDto.setRoleIdList(umsAdminRoleRelations.stream().map(e -> e.getRoleId()).collect(Collectors.toList()));
        return umsAdminDetailDto;
    }

    @Override
    public PageVo<UmsAdminDto> list(String keyword, Integer pageSize, Integer pageNum) {
        PageHelper.startPage(pageNum, pageSize);
        UmsAdminExample example = new UmsAdminExample();
        UmsAdminExample.Criteria criteria = example.createCriteria();
        criteria.andLogicDeleteAtEqualTo(0L);
        if (!StringUtils.isEmpty(keyword)) {
            criteria.andUsernameLike("%" + keyword + "%");
            example.or(example.createCriteria().andNickNameLike("%" + keyword + "%"));
        }
        List<UmsAdmin> umsAdmins = adminMapper.selectByExample(example);

        List<UmsAdminDto> umsAdminDtos = BeanUtil.copyToList(umsAdmins, UmsAdminDto.class);
        for (UmsAdminDto item : umsAdminDtos) {
            UmsAdminRoleRelationExample umsAdminRoleRelationExample = new UmsAdminRoleRelationExample();
            umsAdminRoleRelationExample.createCriteria().andAdminIdEqualTo(item.getId());
            List<Long> roleIds = adminRoleRelationMapper.selectByExample(umsAdminRoleRelationExample).stream().map(e -> e.getRoleId()).collect(Collectors.toList());
            if (!roleIds.isEmpty()) {
                UmsRoleExample umsRoleExample = new UmsRoleExample();
                umsRoleExample.createCriteria().andIdIn(roleIds);
                List<UmsRole> umsRoles = roleMapper.selectByExample(umsRoleExample);
                item.setRoleList(umsRoles);
            }
        }
        return PageVo.restPage(umsAdmins, umsAdminDtos);
    }

    @Override
    public List<UmsAdmin> list() {
        UmsAdminExample example = new UmsAdminExample();
        example.createCriteria().andStatusEqualTo(true).andLogicDeleteAtEqualTo(0L);
        return adminMapper.selectByExample(example);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(UmsAdminUpdateParam admin) {
        this.updateRole(admin.getId(), admin.getRoleIdList());
        adminMapper.updateByPrimaryKeySelective(BeanUtil.toBean(admin, UmsAdmin.class));
        umsAdminCacheService.delAdmin(admin.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        umsAdminDao.logicDelete(id);
        umsAdminCacheService.delAdmin(id);
        UmsAdminRoleRelationExample adminRoleRelationExample = new UmsAdminRoleRelationExample();
        adminRoleRelationExample.createCriteria().andAdminIdEqualTo(id);
        adminRoleRelationMapper.deleteByExample(adminRoleRelationExample);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateRole(Long adminId, List<Long> roleIds) {
        int count = roleIds == null ? 0 : roleIds.size();
        //先删除原来的关系
        UmsAdminRoleRelationExample adminRoleRelationExample = new UmsAdminRoleRelationExample();
        adminRoleRelationExample.createCriteria().andAdminIdEqualTo(adminId);
        adminRoleRelationMapper.deleteByExample(adminRoleRelationExample);
        //建立新关系
        if (!CollectionUtils.isEmpty(roleIds)) {
            List<UmsAdminRoleRelation> list = new ArrayList<>();
            for (Long roleId : roleIds) {
                UmsAdminRoleRelation roleRelation = new UmsAdminRoleRelation();
                roleRelation.setAdminId(adminId);
                roleRelation.setRoleId(roleId);
                list.add(roleRelation);
            }
            adminRoleRelationDao.insertList(list);
        }
        return count;
    }

    @Override
    public List<UmsRole> getRoleList(Long adminId) {
        return adminRoleRelationDao.getRoleList(adminId);
    }

    @Override
    public List<UmsResource> getResourceList(Long adminId,Long menuId) {
        return adminRoleRelationDao.getResourceList(adminId,menuId);
    }

    @Override
    public int updatePassword(UpdateAdminPasswordParam param) {
        if (StrUtil.isEmpty(param.getUsername())
                || StrUtil.isEmpty(param.getOldPassword())
                || StrUtil.isEmpty(param.getNewPassword())) {
            return -1;
        }
        UmsAdminExample example = new UmsAdminExample();
        example.createCriteria().andUsernameEqualTo(param.getUsername()).andLogicDeleteAtEqualTo(0L);
        List<UmsAdmin> adminList = adminMapper.selectByExample(example);
        if (CollUtil.isEmpty(adminList)) {
            return -2;
        }
        UmsAdmin umsAdmin = adminList.get(0);
        if (!BCrypt.checkpw(param.getOldPassword(), umsAdmin.getPassword())) {
            return -3;
        }
        umsAdmin.setPassword(SecureUtil.md5(param.getNewPassword()));
        adminMapper.updateByPrimaryKey(umsAdmin);
        umsAdminCacheService.delAdmin(umsAdmin.getId());
        return 1;
    }

    @Override
    public UserDto loadUserByUsername(String username) {
        //获取用户信息
        UmsAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            List<UmsRole> roleList = getRoleList(admin.getId());
            UserDto userDTO = new UserDto();
            BeanUtils.copyProperties(admin, userDTO);
            if (CollUtil.isNotEmpty(roleList)) {
                List<String> roleStrList = roleList.stream().map(item -> item.getId() + "_" + item.getName()).collect(Collectors.toList());
                userDTO.setRoles(roleStrList);
            }
            return userDTO;
        }
        return null;
    }

    @Override
    public UmsAdmin getCurrentAdmin(HttpServletRequest request) throws Exception {
        UserDto userDto = CurrentUtil.currentUserDto(request);
        UmsAdmin admin = umsAdminCacheService.getAdmin(userDto.getId());
        if (admin != null) {
            return admin;
        } else {
            admin = adminMapper.selectByPrimaryKey(userDto.getId());
            umsAdminCacheService.setAdmin(admin);
            return admin;
        }
    }

 
}
